package Objects;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class MessageParser {

	public synchronized static RoutingTable deadRouterPurge(String RouterID, RoutingTable rt) {
		Map<String, PathInfo> myDestinationMap = rt.getDestination();

		if (rt.getIntraRouters().size() > 0) {
			rt.removeIntraRouter(RouterID);
		}

		if (myDestinationMap.get(RouterID) != null) {
			myDestinationMap.remove(RouterID);
			Iterator<String> it = myDestinationMap.keySet().iterator();
			while (it.hasNext()) {
				String destination = it.next();
				PathInfo pathInfo = myDestinationMap.get(destination);
				ArrayList<Path> backupPaths = pathInfo.getBackupPaths();
				for (int i = 0; i < backupPaths.size(); i++) {
					if (backupPaths.get(i).contains(RouterID)) {
						backupPaths.remove(i);
						i--;
					}
				}
				if (pathInfo.getBestPath().contains(RouterID)) {
					if (backupPaths.size() > 0) {
						int smallestHopCount = Integer.MAX_VALUE;
						int shortestBackupPathIndex = 0;
						for (int i = 0; i < backupPaths.size(); i++) {
							Path backup = backupPaths.get(i);
							int hopCount = backup.getHopCount();
							if (hopCount < smallestHopCount) {
								smallestHopCount = hopCount;
								shortestBackupPathIndex = i;
							}
						}
						pathInfo.setBestPath(pathInfo.getBackupPaths().get(shortestBackupPathIndex));
						pathInfo.getBackupPaths().remove(shortestBackupPathIndex);
						// myDestinationMap.put(destination, pathInfo);
					} else {
						myDestinationMap.remove(destination);
					}
				}
			}
		}
		return rt;
	}

	public static RoutingTable parseIntraMessage(String message, RoutingTable rt) {
		Set<String> intraRouters = new HashSet<String>();
		String[] splitString = message.split(",");
		for (int i = 0; i < splitString.length; i++) {
			splitString[i] = splitString[i].trim();
		}

		String sourceNode = splitString[0];
		// char sourceIG = splitString[1].charAt(0);

		if (rt.getDestination().get(sourceNode) == null)
			return rt;

		for (int i = 2; i < splitString.length - 1; i++) {
			intraRouters.add(splitString[i]);
		}

		rt.getDestination().get(sourceNode).setIntraRouters(intraRouters);
		return rt;
	}

	public static RoutingTable parseUpdateMessage(String message, RoutingTable rt) {
		// Logger.log(Logger.ALL, "Parse Intra update message: \n"+message);
		String destinationNode;
		String sourceNode;
		int hopCount;
		char desig;
		Path tempPath = new Path();
		PathInfo tempPathInfo = new PathInfo();
		PathInfo comparePathInfo = null;
		ArrayList<String> newPath = new ArrayList<String>();
		ConcurrentHashMap<String, PathInfo> destination = rt.getDestination();

		String[] splitString = message.split(",");
		for (int i = 0; i < splitString.length; i++) {
			splitString[i] = splitString[i].trim();
		}
		sourceNode = splitString[0];
		tempPath.setSource(sourceNode);
		destinationNode = splitString[2];
		desig = splitString[3].charAt(0);

		if (rt.getMyIG() == 'g') {
			if (desig != 'g'){
				rt.getIntraRouters().add(destinationNode);
			}
			
		}

		tempPath.setDestination(destinationNode);
		hopCount = Integer.parseInt(splitString[4]);
		tempPath.setHopCount(hopCount);
		for (int i = 5; i < splitString.length - 1; i++) {
			newPath.add(splitString[i]);
		}
		tempPath.setPath(newPath);

		tempPathInfo.setHopCount(hopCount);
		tempPathInfo.setIG(desig);
		tempPathInfo.setBestPath(tempPath);

		tempPathInfo = addMyself(rt.getMyID(), tempPathInfo);

		// if my routing table doesn't even contain my neighbor
		// this will happen if my neighbor router died and came back up
		if (!destination.containsKey(sourceNode)) {
			Path p = new Path();
			PathInfo pi = new PathInfo();
			String sourceID = rt.getMyID();
			String desID = sourceNode;
			Logger.log(Logger.DEBUG, "source id: " + sourceID + " des id: " + desID);
			if (!sourceID.equals(desID)) {
				ArrayList<String> pathArray = new ArrayList<String>();
				pathArray.add(sourceID);
				pathArray.add(desID);
				p.setDestination(desID);
				p.setSource(sourceID);
				p.setHopCount(1);
				p.setPath(pathArray);
				pi.setHopCount(1);
				pi.setIG(desig);
				pi.setBestPath(p);
				destination.put(desID, pi);
				rt.setDestination(destination);
				rt.addIntraRouter(desID);
			}
		}

		// First thing to check: Do I (the router) exist already in this path?
		for (int i = 1; i < newPath.size(); i++) {
			if (rt.getMyID().equals(newPath.get(i))) {
				return rt;
			}
		}

		// Next check to see if there's already a path for the given destination
		if ((comparePathInfo = rt.getDestination().get(destinationNode)) == null) {
			destination.put(destinationNode, tempPathInfo);
			rt.setDestination(destination);
			return rt;
		}

		// Next check to see if the hop count for the updated message is
		// less than the current best hop count for the routing table entry
		if (comparePathInfo.getHopCount() > tempPathInfo.getHopCount()) {
			// Move the current path info to the backup path list
			rt.getDestination().get(destinationNode).addBackupPath(comparePathInfo.getBestPath());
			// Set new path info as current path info
			rt.getDestination().get(destinationNode).setBestPath(tempPath);
			rt.getDestination().get(destinationNode).setHopCount(hopCount);
			return rt;
		}
		// Else add new path to backup paths if this back up path doesn't
		// already exist
		if (!rt.getDestination().get(destinationNode).getBackupPaths().contains(tempPath)
				&& !rt.getDestination().get(destinationNode).getBestPath().equals(tempPath))
			rt.getDestination().get(destinationNode).addBackupPath(tempPath);
		return rt;
	}

	private static PathInfo addMyself(String sourceID, PathInfo old) {
		PathInfo newPathInfo = old;
		newPathInfo.setHopCount(newPathInfo.getHopCount() + 1);
		Path p = newPathInfo.getBestPath();
		p.setHopCount(p.getHopCount() + 1);
		p.setSource(sourceID);
		ArrayList<String> pp = p.getPath();
		pp.add(0, sourceID);
		p.setPath(pp);
		newPathInfo.setBestPath(p);
		return newPathInfo;
	}
}